import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.StringBufferInputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.UIManager;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class Client {
	static final String NAME = "TicketView";
	static final String SERVER_URL = "http://localhost:8080";
	static private QueueInformation[] queues;
	static private QueueAlert[] alerts;
	
	static Map<String, Integer> registeredTickets = new HashMap<String, Integer>();
	
	static void registTicketWithService(String serviceName, String ticketNumber) {
		registTicketWithService(serviceName, Integer.parseInt(ticketNumber));
	}
	
	static void registTicketWithService(String serviceName, int ticketNumber) {
		registeredTickets.put(serviceName, ticketNumber);
	}
	
	static Integer getRegistedTicketWithService(String serviceName) {
		return registeredTickets.get(serviceName);
	}

	public static void main(String args[]) {
		// INTERFACE GRÁFICA
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception ex) {
			// Ignore
		}

		UIManager.put("OptionPane.cancelButtonText", "Cancelar");
		UIManager.put("OptionPane.noButtonText", "Não");
		UIManager.put("OptionPane.okButtonText", "OK");
		UIManager.put("OptionPane.yesButtonText", "Sim");
		try {
			// Obter os detalhes dos serviÃ§os
			getInfoForAllQueues();
			initAlerts();
		} catch (Exception ex) {
			JOptionPane.showMessageDialog(null, "Erro!\n" + ex.getMessage(), NAME, JOptionPane.ERROR_MESSAGE);
			System.exit(1);
		}

		// Mostrar a janela grafica
		FrmMain frmMain = new FrmMain();
		frmMain.setVisible(true);

		// TESTES
		/*URL url = null;
		try {
			url = new URL(SERVER_URL + "/GETBYSERVICE?idService=2");
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			return;
		}
		
		HttpURLConnection connection = null;
		
		try {
			connection = (HttpURLConnection)url.openConnection();
			//System.out.println(conn.getResponseCode());
			
			BufferedInputStream input = new BufferedInputStream(connection.getInputStream());

			String response = new String();

			while (input.available() != 0) {
				response += (char)input.read();
			}

			if (connection.getContentType().startsWith("application/xml")) {
				System.out.println("foo" + response);
				XMLParser foo = new XMLParser();
				
				String bar = foo.parseGetByService(response);
				
				System.out.println(bar);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			return;
		}*/
	}

	static QueueInformation getQueue(int index) {
		return queues[index];
	}

	static QueueInformation[] getQueues() {
		return queues;
	}

	static void initAlerts() {
		int size = queues.length;
		alerts = new QueueAlert[size];
		for(int i = 0; i < size; i++)
			alerts[i] = new QueueAlert();
	}

	static QueueAlert getAlert(int index) {
		return alerts[index];
	}

	static QueueAlert[] getAlerts() {
		return alerts;
	}

	static void getInfoForAllQueues() throws MalformedURLException, IOException {
		URL url = new URL(SERVER_URL + "/GETALL");
		HttpURLConnection connection = (HttpURLConnection)url.openConnection();
		
		BufferedInputStream input = new BufferedInputStream(connection.getInputStream());
		String response = new String();

		/*while (input.available() != 0)
			response += (char)input.read();*/
		int c;
		while((c = input.read()) != -1)
			response += (char)c;

		if (connection.getContentType().startsWith("application/xml")) {
			List<QueueInformation> res = XMLParser.parseGetAll(response);
			final int size = res.size();
			queues = new QueueInformation[size];

			for(QueueInformation q: res) {
				if(q.getId() >= size)
					throw new IOException("Queues ID's in database are not sequential!");
				queues[q.getId()] = q;
			}
		}
	}
	
	static QueueInformation getInfobyServiceId(int idService) throws IOException {
		URL url = new URL(SERVER_URL + "/GETBYSERVICE?idService=" + idService);
		HttpURLConnection connection = (HttpURLConnection)url.openConnection();
		
		BufferedInputStream input = new BufferedInputStream(connection.getInputStream());
		String response = new String();

		/*while (input.available() != 0)
			response += (char)input.read();*/
		int c;
		while((c = input.read()) != -1)
			response += (char)c;

		if (connection.getContentType().startsWith("application/xml")) {
			return XMLParser.parseGetByService(response);
		}
		
		return null;
	}
}

class XMLParser {
	static public QueueInformation parseGetByService(String response) {
		
		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(new StringBufferInputStream(response));
			doc.getDocumentElement().normalize();

			NodeList nList = doc.getElementsByTagName("service");
			
			
			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {

					Element eElement = (Element) nNode;

					String id = getTagValue("id", eElement);
					String code = getTagValue("code", eElement);
					String ticketServedNumber = getTagValue("ticketServedNumber", eElement);
					String ticketServedHour = getTagValue("ticketServedHour", eElement);
					String desk = getTagValue("desk", eElement);
					String lastTicketPrintedNumber = getTagValue("lastTicketPrintedNumber", eElement);
					String lastTicketPrintedHour = getTagValue("lastTicketPrintedHour", eElement);
					String expectedTime = getTagValue("expectedTime", eElement);
					String remainingTickets = getTagValue("remainingTickets", eElement);
					String allRemainingTickets = getTagValue("allRemainingTickets", eElement);

					return new QueueInformation(id, code, ticketServedNumber, ticketServedHour, desk, lastTicketPrintedNumber, lastTicketPrintedHour, expectedTime, remainingTickets, allRemainingTickets);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	static public List<QueueInformation> parseGetAll(String response) {
		List<QueueInformation> queues = new LinkedList<QueueInformation>();
		
		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(new StringBufferInputStream(response));
			doc.getDocumentElement().normalize();

			NodeList nList = doc.getElementsByTagName("queue");

			queues = new LinkedList<QueueInformation>();
			
			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {

					Element eElement = (Element) nNode;

					String id = getTagValue("id", eElement);
					String code = getTagValue("code", eElement);
					String currentTicket = getTagValue("currentTicket", eElement);
					String description = getTagValue("description", eElement);
					String desk = getTagValue("desk", eElement);
					
					queues.add(new QueueInformation(id, code, currentTicket, description, desk));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return queues;
	}

	private static String getTagValue(String sTag, Element eElement) {
		NodeList nlList = eElement.getElementsByTagName(sTag).item(0)
				.getChildNodes();
		Node nValue = (Node) nlList.item(0);
		String value = nValue.getNodeValue();
		if(value.equals("null"))
			value = "N/A";
		return value;
	}
}

class QueueInformation {
	private final int id;
	private final char code;
	
	// ticketServedNumber quando chamado no getByServiceID
	private final String currentTicket;
	private final String description;
	
	// Campo retornados so pelo getByServiceID
	private final String ticketServedHour;
	private final String desk;
	private final String lastTicketPrintedNumber;
	private final String lastTicketPrintedHour;
	private final String expectedTimeToBeServed;
	private final String remainingTicketsToBeServed;
	private final String allRemainingTickets; // tickets que faltam em todas as filas
	
	// Indica se este objecto foi construido com uma chamada para o GetAll ou para o GetByServiceID
	private final boolean constructedByGetAll;

	// Construtor a usar quando o cliente utiliza GETALL no servidor
	public QueueInformation(String id, String code, String currentTicket, String description, String desk) {
		this.id = Integer.parseInt(id);
		this.code = code.charAt(0);
		this.currentTicket = currentTicket;
		this.description = description;
		
		ticketServedHour = null;
		this.desk = desk;
		lastTicketPrintedNumber = null;
		lastTicketPrintedHour = null;
		expectedTimeToBeServed = null;
		remainingTicketsToBeServed = null;
		allRemainingTickets = null;
		
		constructedByGetAll = true;
	}
	
	// Constructor a usar quando o cliente utiliza GetByService no servidor
	public QueueInformation(String id, String code, String ticketServedNumber, String ticketServedHour,
			String desk, String lastTicketPrintedNumber, String lastTicketPrintedHour, String expectedTimeToBeServed, String remainingTickets, String allRemainingTickets) {
		this.id = Integer.parseInt(id);
		this.code = code.charAt(0);
		this.currentTicket = ticketServedNumber;
		this.ticketServedHour = ticketServedHour;
		this.desk = desk;
		this.lastTicketPrintedNumber = lastTicketPrintedNumber;
		this.lastTicketPrintedHour = lastTicketPrintedHour;
		this.expectedTimeToBeServed = expectedTimeToBeServed;
		this.remainingTicketsToBeServed = remainingTickets;
		this.allRemainingTickets = allRemainingTickets;
		
		description = null;
	
		constructedByGetAll = false;
	}
	
	public String getTicketServedHour() {
		return ticketServedHour;
	}

	public String getDesk() {
		return desk;
	}

	public String getLastTicketPrintedNumber() {
		return lastTicketPrintedNumber;
	}

	public String getLastTicketPrintedHour() {
		return lastTicketPrintedHour;
	}

	public String getExpectedTimeToBeServed() {
		return expectedTimeToBeServed;
	}

	public String getRemainingTicketsToBeServed() {
		return remainingTicketsToBeServed;
	}

	public String getAllRemainingTickets() {
		return allRemainingTickets;
	}

	public boolean isConstructedByGetAll() {
		return constructedByGetAll;
	}

	public int getId() {
		return id;
	}

	public char getCode() {
		return code;
	}
	
	public String getCurrentTicket() {
		return currentTicket;
	}

	public String getDescription() {
		return description;
	}
}

class QueueAlert {
	private boolean enabled = false;
	private int ticketNumber = 0;
	private int numberOfTicketsBeforeAlert = 5;

	public void activateAlert(int ticketNumber, int numberOfTicketsBeforeAlert) {
		enable();
		this.ticketNumber = ticketNumber;
		this.numberOfTicketsBeforeAlert = numberOfTicketsBeforeAlert;
	}

	public boolean isEnabled() {
		return enabled;
	}

	public void enable() {
		enabled = true;
	}

	public void disable() {
		enabled = false;
	}

	public void setEnabled(boolean state) {
		enabled = state;
	}

	public int getTicketNumber() {
		return ticketNumber;
	}

	public void setTicketNumber(int n) {
		ticketNumber = n;
	}

	public int getNumberOfTicketsBeforeAlert() {
		return numberOfTicketsBeforeAlert;
	}

	public void setNumberOfTicketsBeforeAlert(int n) {
		numberOfTicketsBeforeAlert = n;
	}
}